#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <zlib.h>
#define STBI_IMPLEMENTATION
#include "../stb_image.h"
#ifdef __WIN32__
#define FN "gameripper.exe"
#else
#define FN "gameripper"
#endif

typedef struct {
    uint32_t    magic;
    uint16_t    version;
    uint16_t    flags;
    uint16_t    method;
    uint32_t    mtime;
    uint32_t    crc32;
    uint32_t    comp;
    uint32_t    orig;
    uint16_t    fnlen;
    uint16_t    extlen;
} __attribute__((packed)) zip_local_t;

uint32_t unicodes[0x10ffff];
uint8_t glyphs[65536*32];

typedef struct {
    int len[128];
    char id[32], *value[128];
} text_t;
text_t *texts = NULL;
int numcol = 0, numrow = 0, allocrow = 0;

int addrow(char *id)
{
    if(numrow + 1 >= allocrow) {
        allocrow += 1024;
        texts = (text_t*)realloc(texts, allocrow * sizeof(text_t));
        if(!texts) { numrow = allocrow = 0; return 0; }
    }
    memset(&texts[numrow], 0, sizeof(text_t));
    strcpy(texts[numrow++].id, id);
    return numrow - 1;
}

int getcol(char *str)
{
    int i;

    if(!texts || !numrow || numcol >= 128) return 0;
    for(i = 0; i < numcol && memcmp(texts[0].value[i], str, 5); i++);
    if(i == numcol) texts[0].value[numcol++] = str;
    return i;
}

int csv2mem(char *csv, int len)
{
    char *cend = csv + len, term, id[32];
    int i, col, row, f = 1;

    /* collect texts into a table */
    numrow = numcol = len = 0;
    while(csv < cend) {
        while(csv < cend && (*csv == '\r' || *csv == '\n')) csv++;
        if(csv >= cend || !*csv) break;
        if(*csv == '\"') csv++;
        for(i = 0; i < 31 && csv < cend && *csv && *csv != ';' && *csv != ',' && *csv != '\t' && *csv != '\"'; csv++, i++)
            id[i] = *csv;
        if(!i) break;
        if(*csv == '\"') csv++;
        id[i] = 0;
        row = addrow(id);
        for(col = 0; *csv == ';' || *csv == ',' || *csv == '\t'; col++) {
            csv++;
            if(*csv != ';' && *csv != ',' && *csv != '\t' && *csv != '\r' && *csv != '\n') {
                if(csv[0] == '\"' && csv[1] == '\"')
                    csv += 2;
                else {
                    if(*csv == '\"') term = *csv++; else term = 0;
                    texts[row].value[col] = csv;
                    for(; csv < cend && *csv && *csv != '\r' && *csv != '\n'; csv++, texts[row].len[col]++, len++) {
                        if(csv[0] == '\"' && csv[1] == '\"') { csv[0] = '\\'; csv++; texts[row].len[col]++; len++; continue; }
                        if(*csv == term || (!term && (*csv == ';' || *csv == ',' || *csv == '\t'))) break;
                    }
                    if(term == '\"' && *csv == '\"') csv++;
                }
            }
        }
        if(f && !numcol) { numcol = col; f = 0; }
    }
    return len;
}

uint8_t* getfile(char *fn, int *size)
{
    FILE *f;
    uint8_t *buf = NULL;

    *size = 0;
    if(!fn || !*fn) return NULL;
    f = fopen(fn, "rb");
    if(f) {
        fseek(f, 0L, SEEK_END);
        *size = (int)ftell(f);
        fseek(f, 0L, SEEK_SET);
        if((buf = (uint8_t*)malloc((*size) + 1))) {
            memset(buf, 0, (*size) + 1);
            *size = (int)fread(buf, 1, *size, f);
            fclose(f);
        }
    }
    return buf;
}

int main(int argc, char **argv)
{
    zip_local_t *zip;
    FILE *f, *h;
    char *hex, *end, *in, *eol, *hme;
    uint8_t *buf, *ptr, *fn, glyph[32];
    uint32_t u;
    int size, i, j, n = 0, m = 0;
    unsigned long int len;

    if(argc == 2) {
        if((buf = getfile(FN, &size))) {
            for(u = 0; u < size; u++)
                if(!memcmp(buf + u, "/home/", 6))
                    memcpy(buf + u, "/not_your_business_/", 20);
            hme = getenv("LOGNAME");
            if(!hme) hme = getenv("USERNAME");
            if(hme) {
                fprintf(stderr, "check %s ", hme); fflush(stderr);
                i = strlen(hme);
                for(u = 0; u + i < size && memcmp(buf + u, hme, i); u++);
                fprintf(stderr, "%s!\r\n", u + i < size ? "ERROR" : "ok");
            }
            if((f = fopen(FN, "wb"))) {
                fwrite(buf, 1, size, f);
                fclose(f);
            }
        }
        return n;
    }
    if(!(f = fopen("../data.h", "wb"))) {
        fprintf(stderr, "unable to write data.h");
        return 1;
    }
    fprintf(f, "/* generated file, DO NOT edit */\n");
    if((buf = getfile("unifont_jp.hex.gz", &size))) {
        in = (char*)buf + 3;
        j = *in++; in += 6;
        if(j & 4) { i = *in++; i += (*in++ << 8); in += i; }
        if(j & 8) { while(*in++ != 0); }
        if(j & 16) { while(*in++ != 0); }
        if(j & 2) in += 2;
        size -= (int)((uintptr_t)in - (uintptr_t)buf);
        hex = stbi_zlib_decode_malloc_guesssize_headerflag(in, size, 65536, &size, 0);
        free(buf);
        memset(glyphs, 0, sizeof(glyphs));
        memset(unicodes, 0, sizeof(unicodes));
        for(in = hex, end = hex + size; in < end; in = eol) {
            while(in < end && (*in == '\r' || *in == '\n')) in++;
            for(eol = in; eol < end && *eol != '\r' && *eol != '\n'; eol++);
            for(u = 0; in < eol && *in != ':'; in++) {
                u <<= 4; u += (*in >= '0' && *in <= '9' ? *in - '0' : *in - 'A' + 10);
            }
            in++;
            if(((uintptr_t)eol - (uintptr_t)in) == 64 && (
                (!memcmp(in, "AAAA0001", 8) && !memcmp(eol - 8, "80005555", 8)) ||
                (!memcmp(in, "55558000", 8) && !memcmp(eol - 8, "0001AAAA", 8)) ||
                (!memcmp(in, "00007FFE", 8) && !memcmp(eol - 8, "7FFE0000", 8)) ||
                (!memcmp(in, "0000FFFF", 8) && !memcmp(eol - 8, "FFFF0000", 8)))) continue;
            memset(glyph, 0, sizeof(glyph));
            for(j = 0; j < 32 && in < eol; j++) {
                glyph[j] = (*in >= '0' && *in <= '9' ? *in - '0' : *in - 'A' + 10) << 4; in++;
                glyph[j] |= (*in >= '0' && *in <= '9' ? *in - '0' : *in - 'A' + 10); in++;
            }
            for(i = 0; i < n && memcmp(&glyphs[i], glyph, j); i += 16);
            if(i >= n) { memcpy(&glyphs[i], glyph, j); n = i + j; }
            unicodes[u] = ((j / 2) << 24) | (i / 16);
            if(u > m) m = u;
        }
        free(hex);
        len = compressBound(sizeof(unicodes));
        buf = (uint8_t*)malloc(len);
        if(buf) {
            compress2(buf, &len, (uint8_t*)unicodes, (m + 1) * 4, 9);
            fprintf(f, "#define UNICODES_LEN %u\nuint8_t unicodes_gz[%lu] = {", (m + 1) * 4, len);
            for(u = 0; u < len; u++) fprintf(f, "%s%u", u ? "," : "", buf[u]);
            fprintf(f, "};\n");
            free(buf);
        }
        len = compressBound(sizeof(glyphs));
        buf = (uint8_t*)malloc(len);
        if(buf) {
            compress2(buf, &len, glyphs, n, 9);
            fprintf(f, "#define GLYPHS_LEN %u\nuint8_t glyphs_gz[%lu] = {", n, len);
            for(u = 0; u < len; u++) fprintf(f, "%s%u", u ? "," : "", buf[u]);
            fprintf(f, "};\n");
            free(buf);
        }
    }
    if((buf = getfile("logo.png", &size))) {
        fprintf(f, "uint8_t logo_png[%u] = {", size);
        for(i = 0; i < size; i++) fprintf(f, "%s%u", i ? "," : "", buf[i]);
        fprintf(f, "};\n");
        free(buf);
    }
    if((buf = getfile("icon.png", &size))) {
        fprintf(f, "uint8_t icon_png[%u] = {", size);
        for(i = 0; i < size; i++) fprintf(f, "%s%u", i ? "," : "", buf[i]);
        fprintf(f, "};\n");
        free(buf);
    }
    if((buf = getfile("unrpyc.zip", &size))) {
        fprintf(f, "uint8_t unrpyc_zip[] = {");
        for(ptr = buf, n = 0; ptr[0] == 'P' && ptr[1] == 'K' && ptr[2] == 3 && ptr[3] == 4;
          ptr += sizeof(zip_local_t) + zip->fnlen + zip->extlen + zip->comp) {
            zip = (zip_local_t*)ptr;
            if(zip->method == 8 && zip->comp && zip->fnlen > 3 && !memcmp(ptr + sizeof(zip_local_t) + zip->fnlen - 3, ".py", 3)) {
                for(j = zip->fnlen - 1, fn = ptr + sizeof(zip_local_t) + 1; fn[-1] != '/'; fn++, j--);
                for(i = 0; i < j && fn[i] != '/'; i++);
                if(i == j || !memcmp(fn, "decompiler", 10)) {
                    fprintf(f, "%s%u,%u", n++ ? "," : "", zip->comp & 0xff, (zip->comp >> 8) & 0xff);
                    fprintf(f, ",%u,%u,%u,%u", zip->orig & 0xff, (zip->orig >> 8) & 0xff, (zip->orig >> 16) & 0xff, j);
                    for(i = 0; i < j; i++) fprintf(f, ",%u", fn[i]);
                    for(u = 0; u < zip->comp; u++) fprintf(f, ",%u", ptr[sizeof(zip_local_t) + zip->fnlen + zip->extlen + u]);
                }
            }
        }
        fprintf(f, ",0,0};\n");
        free(buf);
    }
    if((buf = getfile("texts.csv", &size))) {
        csv2mem((char*)buf, size);
        fprintf(f, "char *i18n[%u] = {", numcol * numrow);
        for(i = 0; i < numcol; i++)
            for(j = 0; j < numrow; j++) {
                fprintf(f, "%s\"", i || j ? "," : "");
                if(texts[j].value[i] && texts[j].len[i] > 0) fwrite(texts[j].value[i], 1, texts[j].len[i], f);
                fprintf(f, "\"");
            }
        fprintf(f, "};");
        if((h = fopen("../texts.h", "wb"))) {
            fprintf(h, "/* generated file, DO NOT edit */\nenum {TEXT_LANG");
            for(j = 1; j < numrow; j++)
                fprintf(h, ",%s", texts[j].id);
            fprintf(h, ",TEXT_LEN};\n");
            fclose(h);
        }
        free(buf);
    }
    fclose(f);
}
